<template>
  <div id="app">
    <!-- 工具栏 -->
    <Toolbar
      class="controls"
      :selectedNote="selectedNote"
      :revertEnable="revertEnable"
      @revert="onRevert"
    />
    <!-- 操作区 -->
    <div id="notebook">
      <!-- 侧栏 -->
      <NoteList
        class="list"
        :notes="notes"
        :selectedNote="selectedNote"
        @add="onAddNote"
        @select="onSelectNote"
      ></NoteList>

      <!-- 编辑区 -->
      <NoteEdit
        class="edit"
        v-if="selectedNote"
        :note="selectedNote"
        @favorite="onFavoriteNote"
        @remove="onRemoveNote"
        @edit="onEdit"
        @contextmenu="onContextMenu"
      ></NoteEdit>
    </div>
  </div>
</template>

<script lang="ts">
import path from 'path'
import { remote, ipcRenderer, shell } from 'electron'
import { Component, Vue, Watch } from 'vue-property-decorator'
import Toolbar from '@/components/Toolbar.vue'
import NoteList from '@/components/NoteList.vue'
import NoteEdit from '@/components/NoteEdit.vue'
import Note from '@/Note'
import Notes from '@/Notes'

const { Menu } = remote

@Component({
  components: {
    Toolbar,
    NoteList,
    NoteEdit,
  },
})
export default class App extends Vue {
  /** 所有笔记 */
  notes: Notes = new Notes()

  /** 选中笔记的 ID */
  selectedId: string | null = null

  /** 当前窗口 */
  currentWindow = remote.getCurrentWindow()

  /** 是否使能“还原”按钮 */
  revertEnable = false

  /**
   * 当前选中的笔记
   */
  get selectedNote(): Note | null {
    let note = null
    if (this.selectedId) {
      note = this.notes.getNoteFromId(this.selectedId)
    }
    return note || null
  }

  // 监视笔记数据变动
  @Watch('notes', { deep: true })
  onNotesChanged() {
    this.saveNotes()
  }

  // 监视当前选中的笔记ID
  @Watch('selectedId')
  onSelectedIdChanged(val: string) {
    localStorage.setItem('selected-id', val)
  }

  /**
   * 恢复文件到刚打开（创建）时的状态
   * @listens Toolbar#revert
   */
  onRevert() {
    const note = this.selectedNote
    note && note.revert()
    this.updateUserInterface()
  }

  /**
   * 创建一个新笔记，并将其添加到笔记数组中
   * @listens NoteList#add
   */
  onAddNote() {
    const note = new Note(this.notes.length) // 新建一个笔记
    this.notes.add(note) // 添加到列表中
  }

  /**
   * 选中一个指定的笔记
   * @listens NoteList#select
   */
  onSelectNote(note: Note) {
    this.selectedId = note.id
    if (note.filePath && !note.originalContent) {
      ipcRenderer.send('read-file', note)
    }
    this.updateUserInterface()
    ipcRenderer.send('note-selected', this.selectedNote)
  }

  /**
   * 对当前笔记进行标星或取消标星
   * @listens NoteEdit#favorite
   */
  onFavoriteNote() {
    if (this.selectedNote) {
      ;(this.selectedNote.favorite as any) ^= true as any
    }
  }

  /**
   * 删除当前选中的笔记
   * 只是从列表中删除，如果笔记已经保存到文件中，文件将仍保留在硬盘中
   * @listens NoteEdit#remove
   */
  onRemoveNote() {
    if (this.selectedNote && confirm('真的要删除这个笔记吗?')) {
      this.notes.delete(this.selectedNote)
      this.selectedId = null
      this.updateUserInterface()
      ipcRenderer.send('note-selected', this.selectedNote)
    }
  }

  /**
   * 笔记内容发生编辑
   * @listens NoteEdit#edit
   */
  onEdit() {
    const note = this.selectedNote
    if (note) {
      note.textChanged = note.content != note.originalContent
    }
    this.updateUserInterface()
  }

  /**
   * 笔记内容区域鼠标右击
   * @listens NoteEdit#contextmenu
   */
  onContextMenu() {
    // 创建上下文菜单
    const createContextMenu = () => {
      return Menu.buildFromTemplate([
        // 定义一个菜单
        {
          label: '打开文件',
          click() {
            ipcRenderer.send('open-file')
          },
        },
        {
          label: '在资源管理器中显示',
          enabled: !!(this.selectedNote && this.selectedNote.filePath),
          click: () =>
            shell.showItemInFolder(
              (this.selectedNote && this.selectedNote.filePath) as string
            ),
        },
        { type: 'separator' },
        { label: '剪切', role: 'cut' },
        { label: '复制', role: 'copy' },
        { label: '粘贴', role: 'paste' },
        { label: '全选', role: 'selectall' },
      ])
    }

    createContextMenu().popup()
  }

  /**
   * 更新用户界面
   * 根据文本内容是否被修改过，界面上会有一个变化
   */
  updateUserInterface() {
    let title = 'markdown editor'
    const note = this.selectedNote
    let isEdited = false

    if (note) {
      if (note.filePath) {
        title = `${path.basename(note.filePath)} - ${title}`
      } else {
        title = `${note.title} - ${title}`
      }
      isEdited = note.textChanged
      if (isEdited) {
        title = `${title} (Edited)`
      }
    }

    this.currentWindow.setTitle(title)
    // 使能按钮
    this.revertEnable = isEdited
  }

  /**
   * 将所有笔记保存到本地存储
   */
  saveNotes() {
    localStorage.setItem('notes', JSON.stringify(this.notes))
  }

  created() {
    // 从本地存储中恢复笔记数据
    const notes = JSON.parse(localStorage.getItem('notes') as string) || []
    for (let item of notes) {
      this.notes.add(new Note(item))
    }
    this.selectedId = localStorage.getItem('selected-id') || null
    const note = this.selectedNote
    if (note && note.filePath && !note.originalContent) {
      ipcRenderer.send('read-file', note)
    }
    this.updateUserInterface()

    // 窗口关闭
    ipcRenderer.on('close', () => {
      if (this.notes.changed) {
        const result = remote.dialog.showMessageBox(this.currentWindow, {
          type: 'warning',
          title: '不保存修改就退出吗?',
          message: '如果你不保存文件的话，所有的修改将会丢失.',
          buttons: ['退出', '取消'],
          cancelId: 1,
          defaultId: 0,
        })

        if (result === 1) return
      }

      this.currentWindow.destroy()
    })

    // 打开的 markdown 文件渲染到界面上
    ipcRenderer.on(
      'file-opened',
      (event: any, filePath: string, content: string) => {
        const note = new Note(filePath, content) // 新建一个笔记
        this.notes.add(note) // 添加到列表中
      }
    )

    ipcRenderer.on('file-read', (event: any, id: string, content?: string) => {
      const note = this.notes.getNoteFromId(id)
      if (note) {
        if (content) {
          note.content = content
          note.originalContent = content
        } else {
          note.content = '无法打开文件'
          note.originalContent = note.content
          note.filePath = null // 文件已经无效
        }
        this.updateUserInterface()
      }
    })

    // 文件已经保存完成
    ipcRenderer.on('file-saved', (event: any, id: string, filePath: string) => {
      const note = this.notes.getNoteFromId(id)
      if (note) {
        note.filePath = filePath
        note.title = path.basename(filePath)
        note.originalContent = note.content
        note.textChanged = false
        this.updateUserInterface()
      }
    })
  }
}
</script>

<style lang="scss">
@import '@/style/base';

.controls {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  padding: 2px 0;
  border-bottom: 1px solid #ddd;
}

#notebook {
  box-sizing: border-box;
  padding-top: 44px;
  height: 100vh;
  > * {
    min-height: 100%;
    max-height: 100%;
    float: left;
  }
  .list {
    width: 20%;
  }
  .edit {
    width: 80%;
  }
}
</style>
